﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Main.cs" company="Scrum for Team System">
//   None
// </copyright>
// <summary>
//   The main.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace ScrumForTeamSystem.Setup.SfTSConfigatorUI
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Drawing;
    using System.IO;
    using System.Linq;
    using System.Windows.Forms;

    using SfTSDeployment;

    /// <summary>
    /// Initializes instance of Main
    /// </summary>
    public partial class Main : Form, IMainUI
    {
        /// <summary>
        /// The main controller.
        /// </summary>
        private readonly MainController controller;

        /// <summary>
        /// The has loaded flag.
        /// </summary>
        private bool hasLoaded;

        /// <summary>
        /// Initializes a new instance of the <see cref="Main"/> class.
        /// </summary>
        /// <param name="args">The args strings.</param>
        public Main(IEnumerable<string> args)
        {
            this.InitializeComponent();

            this.controller = new MainController(args, this);

            if (this.SelectedCollectionChanged != null)
            {
                this.SelectedCollectionChanged(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Occurs when [selected collection changed].
        /// </summary>
        public event EventHandler SelectedCollectionChanged;

        /// <summary>
        /// Occurs when [load collections].
        /// </summary>
        public event EventHandler ListCollections;

        /// <summary>
        /// Occurs when [deploy].
        /// </summary>
        public event EventHandler Deploy;

        /// <summary>
        /// Occurs when [retract].
        /// </summary>
        public event EventHandler Retract;

        /// <summary>
        /// Occurs when [subscribe].
        /// </summary>
        public event EventHandler Subscribe;

        /// <summary>
        /// Occurs when [unsubscribe].
        /// </summary>
        public event EventHandler Unsubscribe;

        /// <summary>
        /// Gets or sets the TFS URL.
        /// </summary>
        /// <value>The TFS URL.</value>
        public string TfsUrl
        {
            get
            {
                return this.txtServerUrl.Text;
            }

            set
            {
                this.txtServerUrl.Text = value;
            }
        }

        /// <summary>
        /// Gets or sets the meta data file path.
        /// </summary>
        /// <value>The meta data file path.</value>
        public string MetaDataFilePath
        {
            get
            {
                return this.txtMetaFile.Text;
            }

            set
            {
                this.txtMetaFile.Text = value;
            }
        }

        /// <summary>
        /// Gets or sets the zip file path.
        /// </summary>
        /// <value>The zip file path.</value>
        public string ZipFilePath
        {
            get
            {
                return this.txtZipFile.Text;
            }

            set
            {
                this.txtZipFile.Text = value;
            }
        }

        /// <summary>
        /// Gets the selected project collection.
        /// </summary>
        /// <value>The selected project collection.</value>
        public string SelectedCollectionName
        {
            get;
            private set;
        }

        /// <summary>
        /// Enables ui interation.
        /// </summary>
        /// <param name="isAllowed">if set to <c>true</c> [is allowed].</param>
        public void EnableUIInteration(bool isAllowed)
        {
            this.mainTabControl.Enabled = isAllowed;
        }

        /// <summary>
        /// Sets the status.
        /// </summary>
        /// <param name="message">The message.</param>
        public void SetStatus(string message)
        {
            Action callback = () =>
            {
                this.StatusLabel.ForeColor = Color.Black;
                this.StatusLabel.Text = message;
            };

            if (this.InvokeRequired)
            {
                this.Invoke(callback);
            }
            else
            {
                callback();
            }
        }

        /// <summary>
        /// Sets the exception status.
        /// </summary>
        /// <param name="exception">The exceptions.</param>
        public void SetStatus(Exception exception)
        {
            var message = string.Empty;

            while (exception != null)
            {
                message = string.Concat(
                    message,
                    string.IsNullOrEmpty(message) ? string.Empty : Environment.NewLine,
                    exception.Message);

                exception = exception.InnerException;
            }

            Action callback = () =>
                {
                    this.StatusLabel.ForeColor = Color.Red;
                    this.StatusLabel.Text = message;
                };

            MessageBox.Show(message, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);

            if (this.InvokeRequired)
            {
                this.Invoke(callback);
            }
            else
            {
                callback();
            }
        }

        /// <summary>
        /// Determines whether this instance can deploy the template to the selected collection.
        /// </summary>
        /// <param name="isAllowed">if set to <c>true</c> [is allowed].</param>
        public void CanDeploy(bool isAllowed)
        {
            this.butDeploy2.Enabled = isAllowed;
            this.butDeploy1.Enabled = isAllowed;
        }

        /// <summary>
        /// Determines whether this instance can retract the template from the selected collection.
        /// </summary>
        /// <param name="isAllowed">if set to <c>true</c> [is allowed].</param>
        public void CanRetract(bool isAllowed)
        {
            this.butRetract2.Enabled = isAllowed;
            this.butRetract1.Enabled = isAllowed;
        }

        /// <summary>
        /// Determines whether this instance can add a subscription.
        /// </summary>
        /// <param name="isAllowed">if set to <c>true</c> [is allowed].</param>
        public void CanSubscribe(bool isAllowed)
        {
            this.butSubscribe.Enabled = isAllowed;
        }

        /// <summary>
        /// Determines whether this instance can unsubscribe.
        /// </summary>
        /// <param name="isAllowed">if set to <c>true</c> if [is allowed].</param>
        public void CanUnsubscribe(bool isAllowed)
        {
            this.butUnsubscribe.Enabled = isAllowed;
        }

        /// <summary>
        /// Sets the collections.
        /// </summary>
        /// <param name="collectionInfoItems">The collection info items.</param>
        public void SetCollectionList(IEnumerable<ProjectCollectionInfo> collectionInfoItems)
        {
            this.lvCollections1.SelectedIndexChanged -= this.CollectionList_SelectedIndexChanged;
            this.lvCollections2.SelectedIndexChanged -= this.CollectionList_SelectedIndexChanged;

            this.lvCollections1.Items.Clear();
            this.lvCollections2.Items.Clear();

            if (collectionInfoItems != null)
            {
                // The quick deploy list needs a valid meta data file path.
                if (File.Exists(this.MetaDataFilePath))
                {
                    this.lvCollections1.BeginUpdate();

                    foreach (var collectionInfo in collectionInfoItems)
                    {
                        var lvi = new ListViewItem(collectionInfo.Name);
                        lvi.SubItems.Add(collectionInfo.TfsUrl);
                        lvi.SubItems.Add(collectionInfo.Online ? "Online" : "Offline");
                        lvi.SubItems.Add(collectionInfo.IsTemplateDeployed(this.MetaDataFilePath) ? "True" : "False");
                        lvi.Selected = collectionInfo.Name.Equals(this.SelectedCollectionName);

                        this.lvCollections1.Items.Add(lvi);
                    }

                    this.lvCollections1.EndUpdate();
                }

                this.lvCollections2.BeginUpdate();

                foreach (var collectionInfo in collectionInfoItems)
                {
                    var lvi = new ListViewItem(collectionInfo.Name);
                    lvi.SubItems.Add(collectionInfo.Online ? "Online" : "OffLine");
                    lvi.SubItems.Add(collectionInfo.TfsUrl);
                    lvi.Selected = collectionInfo.Name.Equals(this.SelectedCollectionName);

                    this.lvCollections2.Items.Add(lvi);
                }

                this.lvCollections2.EndUpdate();
            }

            this.lvCollections1.SelectedIndexChanged += this.CollectionList_SelectedIndexChanged;
            this.lvCollections2.SelectedIndexChanged += this.CollectionList_SelectedIndexChanged;
        }

        /// <summary>
        /// Sets the templates.
        /// </summary>
        /// <param name="enumerable">The enumerable.</param>
        public void SetTemplateList(IEnumerable<TemplateInfo> enumerable)
        {
            this.lvTemplates.Items.Clear();

            if (enumerable == null)
            {
                return;
            }

            this.lvTemplates.BeginUpdate();

            foreach (var template in enumerable)
            {
                var lvi = new ListViewItem(template.Id.ToString());
                lvi.SubItems.Add(template.Name);
                lvi.SubItems.Add(template.IsScrumTemplate ? "Scrum" : "Non Scrum");
                lvi.SubItems.Add(template.ScrumVersion.ToString());

                this.lvTemplates.Items.Add(lvi);
            }

            this.lvTemplates.EndUpdate();
        }

        /// <summary>
        /// Sets the subscriptions.
        /// </summary>
        /// <param name="enumerable">The enumerable.</param>
        public void SetSubscriptionList(IEnumerable<SubscriptionInfo> enumerable)
        {
            this.lvSubscriptions.Items.Clear();

            if (enumerable == null)
            {
                return;
            }

            this.lvSubscriptions.BeginUpdate();

            foreach (var subscription in enumerable)
            {
                var lvi = new ListViewItem(subscription.Id.ToString());
                lvi.SubItems.Add(subscription.EventType);
                lvi.SubItems.Add(subscription.DeliveryPreferenceAddress);
                lvi.SubItems.Add(subscription.ConditionString);
                lvi.SubItems.Add(subscription.Subscriber);
                lvi.SubItems.Add(subscription.DeliveryPreferenceSchedule);
                lvi.SubItems.Add(subscription.DeliveryPreferenceType);
                lvi.SubItems.Add(subscription.Device);
                lvi.SubItems.Add(subscription.Tag);

                this.lvSubscriptions.Items.Add(lvi);
            }

            this.lvSubscriptions.EndUpdate();
        }

        /// <summary>
        /// Handles the Click event of the btnGetTPCs control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void LoadCollections_Click(object sender, EventArgs args)
        {
            if (this.ListCollections != null)
            {
                this.ListCollections(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// The lstv collection_ selected index changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event args.</param>
        private void CollectionList_SelectedIndexChanged(object sender, EventArgs e)
        {
            var listView = sender as ListView;
            if (listView == null)
            {
                return;
            }

            var collectionName = listView.SelectedItems.Count != 0 
                ? listView.SelectedItems[0].Text 
                : string.Empty;

            // Synchronise the other collection list
            var otherList = listView.Equals(this.lvCollections1) ? this.lvCollections2 : this.lvCollections1;

            otherList.SelectedIndexChanged -= this.CollectionList_SelectedIndexChanged;
            foreach (var item in otherList.Items.OfType<ListViewItem>())
            {
                item.Selected = item.Text.Equals(collectionName);
            }

            otherList.SelectedIndexChanged += this.CollectionList_SelectedIndexChanged;

            if (!Equals(this.SelectedCollectionName, collectionName))
            {
                this.SelectedCollectionName = collectionName;

                if (this.SelectedCollectionChanged != null)
                {
                    this.SelectedCollectionChanged(this, EventArgs.Empty);
                }
            }
        }

        /// <summary>
        /// Handles the Click event of the butDeploy2 control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void Deploy_Click(object sender, EventArgs e)
        {
            if (this.Deploy != null)
            {
                this.Deploy(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Handles the Click event of the Retract control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void Retract_Click(object sender, EventArgs e)
        {
            if (this.Retract != null)
            {
                this.Retract(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Handles the Click event of the Subscribe control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void Subscribe_Click(object sender, EventArgs e)
        {
            if (this.Subscribe != null)
            {
                this.Subscribe(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Handles the Click event of the ShowMetaFileSelector control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void ShowMetaFileSelector_Click(object sender, EventArgs e)
        {
            this.openFileDialog1.Title = "Select the Template Meta Data file.";
            var lastIndexOf = this.MetaDataFilePath.LastIndexOf("\\");

            if (lastIndexOf > 0)
            {
                this.openFileDialog1.InitialDirectory = this.MetaDataFilePath.Substring(0, lastIndexOf);
            }

            this.openFileDialog1.Filter = "Meta Data Files|*.xml";
            this.openFileDialog1.FileName = "MetaData.xml";

            if (this.openFileDialog1.ShowDialog(this) == DialogResult.OK)
            {
                this.MetaDataFilePath = this.openFileDialog1.FileName;

                this.UpdateCommands();
            }
        }

        /// <summary>
        /// Handles the Click event of the ShowZipFileSelector control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void ShowZipFileSelector_Click(object sender, EventArgs e)
        {
            this.openFileDialog1.Title = "Select the Template Zip file.";
            var lastIndexOf = this.ZipFilePath.LastIndexOf("\\");

            if (lastIndexOf > 0)
            {
                this.openFileDialog1.InitialDirectory = this.ZipFilePath.Substring(0, lastIndexOf);
            }

            this.openFileDialog1.Filter = "Zip Files|*.zip";
            this.openFileDialog1.FileName = "ProcessTemplate.zip";

            if (this.openFileDialog1.ShowDialog(this) == DialogResult.OK)
            {
                this.ZipFilePath = this.openFileDialog1.FileName;

                this.UpdateCommands();
            }
        }

        /// <summary>
        /// Updates the commands.
        /// </summary>
        private void UpdateCommands()
        {
            if (this.SelectedCollectionChanged != null)
            {
                this.SelectedCollectionChanged(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Handles the Load event of the Main control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void Main_Load(object sender, EventArgs e)
        {
            if (this.hasLoaded)
            {
                return;
            }

            this.hasLoaded = true;

            // If we have valid file paths show the quick setup; otherwise show the advanced.
            var hasValidFilePaths = File.Exists(this.MetaDataFilePath) && File.Exists(this.ZipFilePath);

            this.mainTabControl.SelectedIndex = hasValidFilePaths ? 0 : 1;

            this.butListCollections1.Enabled = hasValidFilePaths;

            if (!hasValidFilePaths)
            {
                this.Height = 540;
            }
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the mainTabControl control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void MainTabControl_SelectedIndexChanged(object sender, EventArgs e)
        {
            var hasValidFilePaths = File.Exists(this.MetaDataFilePath) && File.Exists(this.ZipFilePath);

            this.butListCollections1.Enabled = hasValidFilePaths;
        }

        /// <summary>
        /// Handles the Click event of the butUnsubscribe control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void Unsubscribe_Click(object sender, EventArgs e)
        {
            if (this.Unsubscribe != null)
            {
                this.Unsubscribe(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Handles the Click event of the DeployAndSubscribe control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void DeployAndSubscribe_Click(object sender, EventArgs args)
        {
            if (this.Deploy == null)
            {
                return;
            }

            // Deployment is done via a background thread, so we need to wait until the colleciton is reloaded.
            this.controller.CollectionDetailsLoaded += this.SubscribeCallback;

            this.Deploy(this, EventArgs.Empty);
        }

        /// <summary>
        /// Handles the Click event of the RetractAndUnsubscribe control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void RetractAndUnsubscribe_Click(object sender, EventArgs e)
        {
            if (this.Retract == null)
            {
                return;
            }

            // Retraction is done via a background thread, so we need to wait until the colleciton is reloaded.
            this.controller.CollectionDetailsLoaded += this.UnsubscribeCallback;

            this.Retract(this, EventArgs.Empty);
        }

        /// <summary>
        /// Unsubscribes the callback.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void UnsubscribeCallback(object sender, EventArgs e)
        {
            this.controller.CollectionDetailsLoaded -= this.UnsubscribeCallback;

            if (this.Unsubscribe != null)
            {
                this.Unsubscribe(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Subscribes the call back.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void SubscribeCallback(object sender, EventArgs args)
        {
            this.controller.CollectionDetailsLoaded -= this.SubscribeCallback;

            if (this.Subscribe != null)
            {
                this.Subscribe(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Handles the Clicked event of the Link control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.LinkLabelLinkClickedEventArgs"/> instance containing the event data.</param>
        private void Link_Clicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            var link = sender as LinkLabel;
            if (link == null)
            {
                return;
            }

            var process = new Process { StartInfo = new ProcessStartInfo(link.Text) };

            process.Start();
        }
    }
}